package com.nulldev.util.internal.unsafecompat.impl.legacy;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.JVM.reflections.Reflections;
import com.nulldev.util.internal.unsafecompat.IUnsafe;

import sun.misc.Unsafe;

public class LegacyUnsafe implements IUnsafe {
	private static final Unsafe THE_UNSAFE;
	private static final Class<?>[] _CLASS = new Class<?>[]
		{ java.nio.ByteBuffer.class };

	static {
		try {
			final PrivilegedExceptionAction<Unsafe> action = new PrivilegedExceptionAction<Unsafe>() {
				public Unsafe run() throws Exception {
					final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
					theUnsafe.setAccessible(true);
					return (Unsafe) theUnsafe.get(null);
				}
			};

			THE_UNSAFE = AccessController.doPrivileged(action);
		} catch (Exception e) {
			throw new RuntimeException("Unable to load unsafe", e);
		}
	}

	private static final Reflections ENCAPS_UNSAFE = Reflections.encapsulate(THE_UNSAFE);

	@Override
	public long objectFieldOffset(final Field f) {
		return THE_UNSAFE.objectFieldOffset(f);
	}

	@Override
	public void putOrderedInt(Object o, long offset, int x) {
		THE_UNSAFE.putOrderedInt(o, offset, x);
	}

	@Override
	public boolean compareAndSwapLong(Object o, long offset, long expected, long x) {
		return THE_UNSAFE.compareAndSwapLong(o, offset, expected, x);
	}

	@Override
	public void putOrderedLong(Object o, long offset, long x) {
		THE_UNSAFE.putOrderedLong(o, offset, x);
	}

	@Override
	public boolean tryInvokeCleaner(final ByteBuffer bb) {
		try {
			ENCAPS_UNSAFE.invoke("invokeCleaner", _CLASS, bb);
			return true;
		} catch (Exception ex) {
			if (!(ex instanceof IllegalArgumentException)) {
				/* 9 out of ten times it's "This DirectBuffer object is a slice or duplicate" */
			} else {
				JVM.errorln(ex);
			}
			return false;
		}
	}

	@Override
	public void putObject(Object o, long offset, Object x) {
		THE_UNSAFE.putObject(o, offset, x);
	}

	@Override
	public void putInt(Object o, long offset, int x) {
		THE_UNSAFE.putInt(o, offset, x);
	}

	@Override
	public boolean compareAndSwapInt(Object o, long offset, int expected, int x) {
		return THE_UNSAFE.compareAndSwapInt(o, offset, expected, x);
	}

	@Override
	public boolean compareAndSwapObject(Object o, long offset, Object expected, Object x) {
		return THE_UNSAFE.compareAndSwapObject(o, offset, expected, x);
	}

	@Override
	public void putOrderedObject(Object o, long offset, Object x) {
		THE_UNSAFE.putOrderedObject(o, offset, x);
	}

	@Override
	public int getInt(Object o, long offset) {
		return THE_UNSAFE.getInt(o, offset);
	}

	@Override
	public int arrayBaseOffset(Class<?> arrayClass) {
		return THE_UNSAFE.arrayBaseOffset(arrayClass);
	}

	@Override
	public int arrayIndexScale(Class<?> arrayClass) {
		return THE_UNSAFE.arrayIndexScale(arrayClass);
	}

	@Override
	public void loadFence() {
		THE_UNSAFE.loadFence();
	}

	@Override
	public void storeFence() {
		THE_UNSAFE.storeFence();
	}

	@Override
	public void fullFence() {
		THE_UNSAFE.fullFence();
	}

	@Override
	public Object getAndSetObject(Object o, long offset, Object newValue) {
		return THE_UNSAFE.getAndSetObject(o, offset, newValue);
	}

	@Override
	public long getAndAddLong(Object o, long offset, long delta) {
		return THE_UNSAFE.getAndAddLong(o, offset, delta);
	}

	@Override
	public long getLongVolatile(Object o, long offset) {
		return THE_UNSAFE.getLongVolatile(o, offset);
	}

	@Override
	public int getIntVolatile(Object o, long offset) {
		return THE_UNSAFE.getIntVolatile(o, offset);
	}

	@Override
	public Object getObjectVolatile(Object o, long offset) {
		return THE_UNSAFE.getObjectVolatile(o, offset);
	}

	@Override
	public void putObjectVolatile(Object o, long offset, Object x) {
		THE_UNSAFE.putObjectVolatile(o, offset, x);
	}

	@Override
	public Object getObject(Object o, long offset) {
		return THE_UNSAFE.getObject(o, offset);
	}

	/* */

	@Override
	public byte getByte(long address) {
		return THE_UNSAFE.getByte(address);
	}

	@Override
	public void putByte(long address, byte x) {
		THE_UNSAFE.putByte(address, x);
	}

	@Override
	public int getInt(long address) {
		return THE_UNSAFE.getInt(address);
	}

	@Override
	public void putInt(long address, int x) {
		THE_UNSAFE.putInt(address, x);
	}

	@Override
	public long allocateMemory(long bytes) {
		return THE_UNSAFE.allocateMemory(bytes);
	}

	@Override
	public long reallocateMemory(long address, long bytes) {
		return THE_UNSAFE.reallocateMemory(address, bytes);
	}

	@Override
	public void setMemory(Object o, long offset, long bytes, byte value) {
		THE_UNSAFE.setMemory(o, offset, bytes, value);
	}

	@Override
	public void freeMemory(long address) {
		THE_UNSAFE.freeMemory(address);
	}
}
